#include "SoundPlayer.h"
#include <iostream>
#include <algorithm>
#include "Mp3Decoder.hpp"
#include "OggDecoder.hpp"

#define FRAMES_PER_BUFFER  (512)
static const short MAX_SHORT = 32767;
static const short MIN_SHORT = -32768;

static bool checkPaError(PaError err)
{
    if (err != paNoError)
    {
        printf("Get error : %s", Pa_GetErrorText(err));
        return false;
    }
    return true;
}

static void to_lower(std::string& s)
{
    for (size_t i = 0; i < s.size(); i++)
        s[i] = towlower(s[i]);
}

SoundPlayer::SoundPlayer()
    : _stream(nullptr),
      _decoder(nullptr),
      _valid(false),
      _speed(1.0f),
      _volume(1.0f),
      _current(0)
{
    
}

SoundPlayer::~SoundPlayer()
{
    close();
}

void SoundPlayer::close()
{
    _valid = false;
    _current = 0;

    if (_decoder) 
    {
        delete _decoder;
        _decoder = nullptr;
    }

    if (_stream)
    {
        Pa_CloseStream(_stream);
        _stream = nullptr;
    }
}

void SoundPlayer::setSoundFile(const std::string& filename)
{
    close();

    int pos = filename.find_last_of('.');
    std::string format =filename.substr(pos + 1);
    to_lower(format);
    if (format == "mp3")
        _decoder = new  Mp3Decoder();
    else if (format == "ogg")
        _decoder = new OggDecoder<short>();
    else {
        printf("format [%s] is not supported.\n", format.c_str());
        return;
    }
    
    _sound._hz = 0;
    _sound._pcm.clear();

    if (!_decoder->decode(filename, _sound)) {
        printf("decode [%s] failed.\n", filename.c_str());
        return;
    }

    PaError err = Pa_OpenDefaultStream(&_stream, 0, _sound.channels(), paInt16, _sound._hz, FRAMES_PER_BUFFER, StreamCallback, this);
    if (!checkPaError(err)) {
        return;
    }

    Pa_SetStreamFinishedCallback(_stream, FinishedCallback);

    _valid = true;

    printf("set sound ok.\n");
}

int SoundPlayer::StreamCallback(
    const void *input, void *output,
    unsigned long frameCount,
    const PaStreamCallbackTimeInfo* timeInfo,
    PaStreamCallbackFlags statusFlags,
    void *userData)
{
    SoundPlayer* player = (SoundPlayer*)userData;
    return player->streamCallback(input, output, frameCount, timeInfo, statusFlags);
}

void SoundPlayer::FinishedCallback(void *userData)
{
    SoundPlayer* player = (SoundPlayer*)userData;
    return player->finishedCallback();
}

void SoundPlayer::finishedCallback()
{
    _current = 0;
}

int SoundPlayer::streamCallback(const void *input, void *output,
    unsigned long frameCount,
    const PaStreamCallbackTimeInfo* timeInfo,
    PaStreamCallbackFlags statusFlags)
{
    short *out = (short*)output;

    (void)timeInfo; /* Prevent unused variable warnings. */
    (void)statusFlags;
    (void)input;

    if (_current + frameCount >= _sound.length())
    {
        return 1;
    }

    for (unsigned long i = 0; i < frameCount; i++)
    {
        for (int j = 0; j < _sound.channels(); j++)
        {
            size_t idx = (_current + i) * _speed;
            if (idx >= _sound.length())
                return 1;

            short data = _sound(j, idx) * _volume;
            data = (std::min)(data, MAX_SHORT);
            data = (std::max)(data, MIN_SHORT);
            *out++ = data;
        }
    }
    _current += frameCount;

    return 0;
}

void SoundPlayer::play() 
{
    if (_valid)
    {
        if (!Pa_IsStreamActive(_stream))
        {
            _current = 0;
            Pa_StartStream(_stream);
        }
    }
}

void SoundPlayer::stop()
{
    if (_valid)
    {
        _current = 0;
        Pa_AbortStream(_stream);
    }
}

void SoundPlayer::pause()
{
    if (_valid) {
        Pa_AbortStream(_stream);
    }
}

void SoundPlayer::resume()
{
    if (_valid) {
        Pa_StartStream(_stream);
    }
}

void SoundPlayer::setSpeed(float speed)
{
    _speed = speed;
}

float SoundPlayer::speed() const
{
    return _speed;
}

void SoundPlayer::setVolume(float volume)
{
    _volume = volume;
}
float SoundPlayer::volume() const
{
    return _volume;
}